package com.example.diffservice.controller;

import com.alibaba.fastjson.JSONObject;
import com.example.api.service.CodeQualityService;
import com.example.common.constant.Constants;
import com.example.common.core.AjaxResult;
import com.example.common.entity.PRInfo;
import com.example.common.utils.GithubApiUtils;
import com.example.diffservice.algorithm.MyDiff;
import com.example.diffservice.entity.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.URI;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author DestinyCc
 * @date 2022/3/28
 */
@RestController
@RequestMapping("/diff")
@Slf4j
public class DiffController {
    @Autowired
    RestTemplate restTemplate;

    @Reference
    CodeQualityService codeQualityService;

    @GetMapping("/getPRList")
    public AjaxResult getPRList(@RequestParam("url") String url){
        //https://github.com/spring-projects/spring-framework.git
        String[] data = url.replaceFirst("https://", "").replace(".git", "").split("/");
        //github.com/spring-projects/spring-framework
        String owner = data[1];
        String repo = data[2];
        log.info("owner = {}, repo = {}", owner, repo);
        String requestUrl = String.format(Constants.GITHUB.GET_PULL_REQUEST_LIST, owner, repo);
        log.info("requestUrl = {}", requestUrl);
        List<PullRequestData> result = new ArrayList<>();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
        for (int i = 1; i < Integer.MAX_VALUE; i++) {
            UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(requestUrl);
            builder.queryParam("page", i);
            URI uri = builder.build(true).toUri();
            log.info("uri = {}", uri.toString());
            List<LinkedHashMap<String, Object>> prList = (List<LinkedHashMap<String, Object>>) GithubApiUtils.callGithubApi(uri.toString());
            if (prList.size() == 0){
                break;
            }
            prList.stream().map(pr ->{
                return new PullRequestData(pr.get("number").toString(),
                        pr.get("state").toString(),
                        pr.get("title").toString(),
                        pr.get("body") == null ? "": pr.get("body").toString(),
                        LocalDateTime.parse(pr.get("created_at").toString(), formatter).plusHours(8),
                        LocalDateTime.parse(pr.get("updated_at").toString(), formatter).plusHours(8));
            }).filter(Objects::nonNull).forEach(result::add);
        }
        log.info("pull size = {}", result.size());
        return AjaxResult.success(result);
    }


    @PostMapping("/calculate")
    public AjaxResult calculate(@RequestBody PRInfo prInfo) {
        String requestUrl = String.format(Constants.GITHUB.GET_COMMITS_URL, prInfo.getOwner(), prInfo.getRepo(), prInfo.getPullNumber());
        log.info(requestUrl);
        List<LinkedHashMap<String, Object>> commitList = (List<LinkedHashMap<String, Object>>) GithubApiUtils.callGithubApi(requestUrl);
        int commitNumber = commitList.size();
        List<String> parentId = new ArrayList<>();
        String commitId;
        String author;
        if (commitNumber == 1){
            LinkedHashMap<String, Object> commitHashmap = commitList.get(0);
            commitId = (String) commitHashmap.get("sha");
            List<LinkedHashMap<String, String>> parents = (List<LinkedHashMap<String, String>>) commitHashmap.get("parents");
            for (LinkedHashMap<String, String> parent: parents){
                parentId.add(parent.get("sha"));
            }
            LinkedHashMap<String, Object> authorInfo = (LinkedHashMap<String, Object>) commitHashmap.get("author");
            author = Objects.equals((String) authorInfo.get("login"), "dependabot[bot]") ? prInfo.getOwner() : (String) authorInfo.get("login");
        }else {
            LinkedHashMap<String, Object> firstCommit = commitList.get(0);
            List<LinkedHashMap<String, String>> parents = (List<LinkedHashMap<String, String>>) firstCommit.get("parents");
            for (LinkedHashMap<String, String> parent: parents){
                parentId.add(parent.get("sha"));
            }
            LinkedHashMap<String, Object> lastCommit = commitList.get(commitList.size()-1);
            commitId = (String) lastCommit.get("sha");
            LinkedHashMap<String, Object> authorInfo = (LinkedHashMap<String, Object>) lastCommit.get("author");
            author = Objects.equals((String) authorInfo.get("login"), "dependabot[bot]") ? prInfo.getOwner() : (String) authorInfo.get("login");
        }
        log.info("commitId = {}, parentId = {}", commitId, parentId);
        MyDiff diff = new MyDiff();
//        diff.run(author, prInfo.getPullNumber(), commitId, parentId, prInfo.getRepo(), commitList.size()==1 ? 0 : 1);
        diff.newRun(prInfo.getOwner(), author, prInfo.getPullNumber(), commitId, parentId, prInfo.getRepo(), commitList.size()==1 ? 0 : 1);
        String prevDir = Constants.DEFAULT_OUTPUT_DIR + prInfo.getRepo() + "/" + prInfo.getPullNumber().toString() + "/prev/";
        log.info("oldDir = {}", prevDir);
        String currDir = Constants.DEFAULT_OUTPUT_DIR + prInfo.getRepo() + "/" + prInfo.getPullNumber().toString() + "/curr/";
        log.info("newDir = {}", currDir);
        UriComponentsBuilder builder = UriComponentsBuilder.fromUriString("http://localhost:8089/decompose");
        builder.queryParam("oldDir", prevDir).queryParam("newDir", currDir);
        URI uri = builder.build(true).toUri();
        JSONObject jsonObject = restTemplate.getForObject(uri, JSONObject.class);
        HashMap<String, List<String>> groups = jsonObject.getObject("result", HashMap.class);
        List<Group> groupList = groups.entrySet().stream().map(g-> new Group(g.getKey(), g.getValue())).collect(Collectors.toList());
        ResultGenerator resultGenerator = new ResultGenerator(commitId, author, prInfo.getPullNumber(), prInfo.getRepo());
        DiffResult diffResult = resultGenerator.generate();
        Result result = new Result();
        result.setDifferences(diffResult.getDifferences());
        result.setLinks(diffResult.getLinks());
        result.setGroups(groupList);
        return AjaxResult.success(result);
    }


    @PostMapping("/getDiff")
    public AjaxResult calculateDiff(@RequestBody PRInfo prInfo) {
        String requestUrl = String.format(Constants.GITHUB.GET_COMMITS_URL, prInfo.getOwner(), prInfo.getRepo(), prInfo.getPullNumber());
        log.info(requestUrl);
        List<LinkedHashMap<String, Object>> commitList = (List<LinkedHashMap<String, Object>>) GithubApiUtils.callGithubApi(requestUrl);
        int commitNumber = commitList.size();
        List<String> parentId = new ArrayList<>();
        String commitId;
        String author;
        if (commitNumber == 1){
            LinkedHashMap<String, Object> commitHashmap = commitList.get(0);
            commitId = (String) commitHashmap.get("sha");
            List<LinkedHashMap<String, String>> parents = (List<LinkedHashMap<String, String>>) commitHashmap.get("parents");
            for (LinkedHashMap<String, String> parent: parents){
                parentId.add(parent.get("sha"));
            }
            LinkedHashMap<String, Object> authorInfo = (LinkedHashMap<String, Object>) commitHashmap.get("author");
            author = Objects.equals((String) authorInfo.get("login"), "dependabot[bot]") ? prInfo.getOwner() : (String) authorInfo.get("login");
        }else {
            LinkedHashMap<String, Object> firstCommit = commitList.get(0);
            List<LinkedHashMap<String, String>> parents = (List<LinkedHashMap<String, String>>) firstCommit.get("parents");
            for (LinkedHashMap<String, String> parent: parents){
                parentId.add(parent.get("sha"));
            }
            LinkedHashMap<String, Object> lastCommit = commitList.get(commitList.size()-1);
            commitId = (String) lastCommit.get("sha");
            LinkedHashMap<String, Object> authorInfo = (LinkedHashMap<String, Object>) lastCommit.get("author");
            author = Objects.equals((String) authorInfo.get("login"), "dependabot[bot]") ? prInfo.getOwner() : (String) authorInfo.get("login");
        }
        log.info("commitId = {}, parentId = {}", commitId, parentId);
        MyDiff diff = new MyDiff();
//        diff.run(author, prInfo.getPullNumber(), commitId, parentId, prInfo.getRepo(), commitList.size()==1 ? 0 : 1);
        diff.newRun(prInfo.getOwner(), author, prInfo.getPullNumber(), commitId, parentId, prInfo.getRepo(), commitList.size()==1 ? 0 : 1);
        ResultGenerator resultGenerator = new ResultGenerator(commitId, author, prInfo.getPullNumber(), prInfo.getRepo());
        DiffResult diffResult = resultGenerator.generate();
        return AjaxResult.success(diffResult);
    }

    @PostMapping("/getDiffWithCodeQuality")
    public AjaxResult calculateDiffWithCodeQuality(@RequestBody PRInfo prInfo) {
        String requestUrl = String.format(Constants.GITHUB.GET_COMMITS_URL, prInfo.getOwner(), prInfo.getRepo(), prInfo.getPullNumber());
        log.info(requestUrl);
        List<LinkedHashMap<String, Object>> commitList = (List<LinkedHashMap<String, Object>>) GithubApiUtils.callGithubApi(requestUrl);
        int commitNumber = commitList.size();
        List<String> parentId = new ArrayList<>();
        String commitId;
        String author;
        if (commitNumber == 1){
            LinkedHashMap<String, Object> commitHashmap = commitList.get(0);
            commitId = (String) commitHashmap.get("sha");
            List<LinkedHashMap<String, String>> parents = (List<LinkedHashMap<String, String>>) commitHashmap.get("parents");
            for (LinkedHashMap<String, String> parent: parents){
                parentId.add(parent.get("sha"));
            }
            LinkedHashMap<String, Object> authorInfo = (LinkedHashMap<String, Object>) commitHashmap.get("author");
            author = Objects.equals((String) authorInfo.get("login"), "dependabot[bot]") ? prInfo.getOwner() : (String) authorInfo.get("login");
        }else {
            LinkedHashMap<String, Object> firstCommit = commitList.get(0);
            List<LinkedHashMap<String, String>> parents = (List<LinkedHashMap<String, String>>) firstCommit.get("parents");
            for (LinkedHashMap<String, String> parent: parents){
                parentId.add(parent.get("sha"));
            }
            LinkedHashMap<String, Object> lastCommit = commitList.get(commitList.size()-1);
            commitId = (String) lastCommit.get("sha");
            LinkedHashMap<String, Object> authorInfo = (LinkedHashMap<String, Object>) lastCommit.get("author");
            author = Objects.equals((String) authorInfo.get("login"), "dependabot[bot]") ? prInfo.getOwner() : (String) authorInfo.get("login");
        }
        log.info("commitId = {}, parentId = {}", commitId, parentId);
        MyDiff diff = new MyDiff();
//        diff.run(author, prInfo.getPullNumber(), commitId, parentId, prInfo.getRepo(), commitList.size()==1 ? 0 : 1);
        diff.newRun(prInfo.getOwner(), author, prInfo.getPullNumber(), commitId, parentId, prInfo.getRepo(), commitList.size()==1 ? 0 : 1);
        ResultGenerator resultGenerator = new ResultGenerator(commitId, author, prInfo.getPullNumber(), prInfo.getRepo());
        DiffResult diffResult = resultGenerator.generate();
        DiffWithCodeQuality diffWithCodeQuality = new DiffWithCodeQuality();
        diffWithCodeQuality.setDifferences(diffResult.getDifferences());
        diffWithCodeQuality.setLinks(diffResult.getLinks());
        String repoPath = Constants.DEFAULT_OUTPUT_DIR + prInfo.getRepo() + "/" + prInfo.getPullNumber();
        String remotePath = prInfo.getOwner() + "/" + prInfo.getRepo();
        diffWithCodeQuality.setCodeQualities(codeQualityService.getPRCodeQuality(repoPath, remotePath));
        return AjaxResult.success(diffWithCodeQuality);
    }

    @GetMapping("/getDiffFiles")
    public AjaxResult getDiffFiles(@RequestParam("filename")String filename,
                                   @RequestParam("repo") String repo,
                                   @RequestParam("pullNumber") String pullNumber){
        String basePath = Constants.DEFAULT_OUTPUT_DIR + repo + "/" + pullNumber + "/";
        Map<String, String> fileContent = new HashMap<>();
        String prevFilePath = basePath + "prev/" + filename;
        fileContent.put("prev", getFile(prevFilePath));
        String currFilePath = basePath + "curr/" + filename;
        fileContent.put("curr", getFile(currFilePath));
        return AjaxResult.success(fileContent);
    }

    public String getFile(String path) {
        File file = new File(path);
        StringBuilder content = new StringBuilder();
        if(!file.exists()){
            log.info("文件不存在");
            return "";
        }
        if(file.isDirectory()){
            log.info("该文件为文件夹");
            return null;
        }
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader(file));
            String tempString = null;
            while ((tempString = reader.readLine()) != null) {
                content.append(tempString);
                content.append("\n");
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                }
            }
        }
        return content.toString();
    }
}
